package team.seekers.oj.codetest.service;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import team.seekers.oj.auth.enums.PermissionEnum;
import team.seekers.oj.contest.client.ProblemClient;
import team.seekers.oj.codetest.dao.CodeTestDao;
import team.seekers.oj.codetest.dao.CodeTestDescriptionDao;
import team.seekers.oj.codetest.dao.CodeTestListDao;
import team.seekers.oj.codetest.dto.CodeTestCreateReqDTO;
import team.seekers.oj.codetest.dto.CodeTestListDTO;
import team.seekers.oj.codetest.dto.CodeTestListReqDTO;
import team.seekers.oj.codetest.dto.CodeTestManageDTO;
import team.seekers.oj.codetest.entity.dao.CodeTestDO;
import team.seekers.oj.codetest.entity.dao.CodeTestDOField;
import team.seekers.oj.codetest.entity.dao.CodeTestDescriptionDO;
import team.seekers.oj.codetest.entity.dao.CodeTestListDo;
import team.seekers.oj.codetest.entity.vo.CodeTestInfoVO;
import team.seekers.oj.codetest.entity.vo.CodeTestProblemListVO;
import team.seekers.oj.codetest.utils.ListToByteUtil;
import team.seekers.oj.common.entity.PageResult;
import team.seekers.oj.common.entity.UserSessionDTO;
import team.seekers.oj.common.enums.ApiExceptionEnum;
import team.seekers.oj.common.exception.ApiException;
import team.seekers.oj.common.util.AssertUtils;
import team.seekers.oj.contest.client.UserClient;
import team.seekers.oj.problem.dto.ProblemForCodeTestDTO;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class CodeTestService {

    @Autowired
    private CodeTestListDao codeTestListDao;

    @Autowired
    private CodeTestDao codeTestDao;

    @Autowired
    private CodeTestDescriptionDao codeTestDescriptionDao;

    @Autowired
    private ProblemClient problemClient;

    @Autowired
    private UserClient userClient;



    /**
     * create a codeTest
     * @param reqDTO
     * @return
     */
    public Long create(CodeTestCreateReqDTO reqDTO) {
        CodeTestDO codeTestDO = new CodeTestDO();
        BeanUtils.copyProperties(reqDTO,codeTestDO);
        List<Long> problems = reqDTO.getProblems();
        byte[] bytes = ListToByteUtil.codeTestProblemsFromProblemIdList(problems);
        codeTestDO.setProblems(bytes);
        int length = 0;
        if(bytes != null){
            length = bytes.length / 8;
        }
        codeTestDO.setProblemNum(length);
        AssertUtils.isTrue(codeTestDao.save(codeTestDO), ApiExceptionEnum.UNKNOWN_ERROR);
        return codeTestDO.getId();
    }

    /**
     * get codeTest Info
     * @param codeTestId
     * @return
     */

    public CodeTestInfoVO getVO(long codeTestId) {
        CodeTestDO codeTestDO = codeTestDao.getById(codeTestId);
        System.out.println(ListToByteUtil.codeTestUsersToUserIdList(codeTestDO.getUsers()));
        AssertUtils.notNull(codeTestDO, ApiExceptionEnum.CODETEST_NOT_FOUND);
        CodeTestInfoVO codeTestInfoVO = new CodeTestInfoVO();

        Long descriptionId = codeTestDO.getDescriptionId();
        CodeTestDescriptionDO codeTestDescriptionDO = codeTestDescriptionDao.getById(descriptionId);
        String description = codeTestDescriptionDO.getDescription();
        codeTestInfoVO.setDescription(description);

        BeanUtils.copyProperties(codeTestDO,codeTestInfoVO);
        List<Long> problemIds = codeTestDao.codeTestProblemsToProblemIdList(codeTestDO.getProblems());
//        List<Long> problemIds = new ArrayList<>();
//        problemIds.add(1L);
        List<ProblemForCodeTestDTO> problemForCodeTestDTOS = problemClient.queryProblemsByIds(problemIds);
        List<CodeTestProblemListVO> codeTestProblemListVOS =  new ArrayList<>();
        for(ProblemForCodeTestDTO problemForCodeTestDTO : problemForCodeTestDTOS){
            CodeTestProblemListVO codeTestProblemListVO = new CodeTestProblemListVO();
            BeanUtils.copyProperties(problemForCodeTestDTO,codeTestProblemListVO);
            codeTestProblemListVOS.add(codeTestProblemListVO);
        }
        codeTestInfoVO.setProblemIds(problemIds);
        codeTestInfoVO.setProblems(codeTestProblemListVOS);
        return codeTestInfoVO;
    }

    public CodeTestManageDTO getDTO(long codeTestId) {
        CodeTestDO codeTestDO = codeTestDao.getById(codeTestId);
        AssertUtils.notNull(codeTestDO, ApiExceptionEnum.CODETEST_NOT_FOUND);
        CodeTestManageDTO codeTestManageDTO = new CodeTestManageDTO();
        BeanUtils.copyProperties(codeTestDO,codeTestManageDTO);

        byte[] problems = codeTestDO.getProblems();
        List<Long> longs = ListToByteUtil.codeTestProblemsToProblemIdList(problems);
        codeTestManageDTO.setProblems(longs);
        System.out.println(longs);

        byte[] users = codeTestDO.getUsers();
        List<Long> userList = ListToByteUtil.codeTestUsersToUserIdList(users);
        codeTestManageDTO.setUsers(userList);
        System.out.println(userList);

        return codeTestManageDTO;
    }

    public void update(CodeTestManageDTO reqDTO) {
        CodeTestDO codeTestDO = codeTestDao.lambdaQuery().select(
                CodeTestDO::getId,
                CodeTestDO::getUserId,
                CodeTestDO::getVersion
        ).eq(CodeTestDO::getId, reqDTO.getId()).one();
        AssertUtils.notNull(codeTestDO, ApiExceptionEnum.CONTEST_NOT_FOUND);

        CodeTestDO codeTeatUpdateDO = new CodeTestDO();
        BeanUtils.copyProperties(reqDTO,codeTeatUpdateDO);

        List<Long> reqDTOProblems = reqDTO.getProblems();
        if(reqDTOProblems==null){
            reqDTOProblems = new ArrayList<>();
        }
        if(reqDTOProblems.size() != 0){
            byte[] bytes = ListToByteUtil.codeTestProblemsFromProblemIdList(reqDTOProblems);
            codeTeatUpdateDO.setProblems(bytes);
            byte[] problems = codeTeatUpdateDO.getProblems();
            int length = 0;
            if(problems != null){
                length = problems.length / 8;
            }
            codeTeatUpdateDO.setProblemNum(length);
        }

        List<Long> reqDTOUsers = reqDTO.getUsers();
        if(reqDTOUsers==null){
            reqDTOUsers = new ArrayList<>();
        }
        if(reqDTOUsers.size() != 0){
            byte[] byteUsers = ListToByteUtil.codeTestUsersFromUserIdList(reqDTOUsers);
            codeTeatUpdateDO.setUsers(byteUsers);
            byte[] users = codeTeatUpdateDO.getUsers();
            int size = 0;
            if(users != null){
                size = users.length / 8;
            }
            codeTeatUpdateDO.setUserNum(size);
        }

        codeTeatUpdateDO.setVersion(codeTestDO.getVersion());
        UpdateWrapper<CodeTestDO> updater = new UpdateWrapper<>();
        updater.eq(CodeTestDOField.ID, codeTeatUpdateDO.getId());
        if (!codeTestDao.update(codeTeatUpdateDO, updater)) {
            throw new ApiException(ApiExceptionEnum.SERVER_BUSY);
        }
    }

    public boolean isCodeTestManager(Long codeTestCreatorId, UserSessionDTO userSessionDTO) {
        return PermissionEnum.SUPERADMIN.in(userSessionDTO) ||
                userSessionDTO.userIdEquals(codeTestCreatorId);
    }

    public void addProblem(long codeTestId, Long problemId) {
        // 判断problem是否存在
        AssertUtils.isTrue(problemClient.queryProblemsById(problemId) != null,
                ApiExceptionEnum.PROBLEM_NOT_FOUND);

        CodeTestManageDTO query = getDTO(codeTestId);
        CodeTestManageDTO codeTestManageDTO = new CodeTestManageDTO();
        codeTestManageDTO.setId(codeTestId);
        if(query.getProblems() != null){
            List<Long> problems = query.getProblems();
            problems.add(problemId);
            codeTestManageDTO.setProblems(problems);
        }else{
            List<Long> problems = new ArrayList<>();
            problems.add(problemId);
            codeTestManageDTO.setProblems(problems);
        }
        update(codeTestManageDTO);
    }

    public void addUser(long codeTestId, Long userId) {
        // 判断用户是否存在
        AssertUtils.isTrue(userClient.userIdToUser(userId) != null,
                ApiExceptionEnum.USER_NOT_FOUND);

        CodeTestManageDTO query = getDTO(codeTestId);
        if(query.getUsers() != null){
            List<Long> users = query.getUsers();
            users.add(userId);
            query.setUsers(users);
        }else{
            List<Long> users = new ArrayList<>();
            users.add(userId);
            query.setUsers(users);
        }
        update(query);
    }


    public void removeProblem(long codeTestId, Long problemId) {
        CodeTestManageDTO query = getDTO(codeTestId);
        List<Long> queryProblems = query.getProblems();
        if(queryProblems == null){
            queryProblems = new ArrayList<>();
        }
        if(queryProblems.size() != 0){
            List<Long> problems = query.getProblems();
            boolean remove = problems.remove(problemId);
            if(!remove){
                throw new ApiException(ApiExceptionEnum.CODETEST_NOT_IN);
            }
            query.setProblems(problems);
            System.out.println(problems);
        }else{
            throw new ApiException(ApiExceptionEnum.PROBLEM_NOT_FOUND);
        }
        update(query);
    }

    public void removeUser(long codeTestId, Long userId) {
        CodeTestManageDTO query = getDTO(codeTestId);
        List<Long> queryUsers = query.getUsers();
        if(queryUsers == null){
            queryUsers = new ArrayList<>();
        }
        if(queryUsers.size() != 0){
            List<Long> users = query.getUsers();
            System.out.println(users);
            boolean remove = users.remove(userId);
            if(!remove){
                throw new ApiException(ApiExceptionEnum.CODETEST_NOT_IN);
            }
            query.setUsers(users);
        }else{
            throw new ApiException(ApiExceptionEnum.USER_NOT_FOUND);
        }
        update(query);
    }

    public void removeCodeTest(long codeTestId) {
        CodeTestManageDTO codeTestManageDTO = getDTO(codeTestId);
        codeTestManageDTO.setDeleted(1);
        System.out.println(codeTestManageDTO.getDeleted());
        update(codeTestManageDTO);
    }

    public List<Long> queryIdToProblemIds(Long codeTestId) {
        CodeTestDO codeTestDO = codeTestDao.lambdaQuery().select(
                CodeTestDO::getId,
                CodeTestDO::getVersion,
                CodeTestDO::getProblems
        ).eq(CodeTestDO::getId, codeTestId).one();
        AssertUtils.notNull(codeTestDO, ApiExceptionEnum.CODETEST_NOT_FOUND);
        return codeTestDao.codeTestProblemsToProblemIdList(codeTestDO.getProblems());
    }

    public PageResult<CodeTestListDTO> page(CodeTestListReqDTO reqDTO) {
        LambdaQueryChainWrapper<CodeTestListDo> query = codeTestListDao.lambdaQuery()
                .orderByDesc(CodeTestListDo::getGmtModified);

        Page<CodeTestListDo> pageResult = query.page(new Page<>(reqDTO.getPageNow(), reqDTO.getPageSize()));
        List<CodeTestListDo> records = pageResult.getRecords();
        List<CodeTestListDTO> res = new ArrayList<>();
        for(CodeTestListDo item : records){
            CodeTestListDTO codeTestListDTO = new CodeTestListDTO();
            BeanUtils.copyProperties(item,codeTestListDTO);
            res.add(codeTestListDTO);
        }
        return new PageResult<>(pageResult.getPages(), res);
    }

    public PageResult<CodeTestListDTO> pageCondition(CodeTestListReqDTO reqDTO) {
        LambdaQueryChainWrapper<CodeTestListDo> query = codeTestListDao.lambdaQuery()
                .orderByDesc(CodeTestListDo::getGmtModified).like(CodeTestListDo::getCodeTestName,reqDTO.getCondition());

        Page<CodeTestListDo> pageResult = query.page(new Page<>(reqDTO.getPageNow(), reqDTO.getPageSize()));
        List<CodeTestListDo> records = pageResult.getRecords();
        List<CodeTestListDTO> res = new ArrayList<>();
        for(CodeTestListDo item : records){
            CodeTestListDTO codeTestListDTO = new CodeTestListDTO();
            BeanUtils.copyProperties(item,codeTestListDTO);
            res.add(codeTestListDTO);
        }
        return new PageResult<>(pageResult.getPages(), res);
    }





}
